//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-12-04     ZhangYihua   first version
//
// Description  : 
//################################################################################

module interp_n #(
parameter           CDW                     = 16,
parameter           CFW                     = 15,
parameter           IDW                     = 12,
parameter           IFW                     = 9,
parameter           ODW                     = 12,
parameter           OFW                     = 9,
parameter           TAP_NUM                 = 15,       // must TAP_NUM>=2
parameter           COE_NUM                 = TAP_NUM/2 + TAP_NUM%2,
parameter           CSCD_LEN_MAX            = 4,    // CSCD_LEN_MAX>=2, cascade length, if too bigger, bad Fmax
parameter           COE_ZERO_MAP            = {COE_NUM{1'b0}},  // for saving area
parameter           COE_ZERO_NUM            = bit_sum_f(COE_ZERO_MAP),
parameter           PRDCT_NUM               = COE_NUM-COE_ZERO_NUM,
parameter           CSCD_GRP                = PRDCT_NUM/CSCD_LEN_MAX + (((PRDCT_NUM%CSCD_LEN_MAX)==0) ? 0 : 1),
parameter           CSCD_LEN                = PRDCT_NUM/CSCD_GRP + (((PRDCT_NUM%CSCD_GRP)==0) ? 0 : 1),
parameter           LVL_NUM                 = (CSCD_GRP<=1) ? 1 : $clog2(CSCD_GRP),
parameter [LVL_NUM-1:0]     LVL_REG         = {LVL_NUM{1'b0}}   // specify register for each level
) ( 
input                                       rst_n,
input                                       clk,
input                                       cke,
input                                       clk_nx,     // rising edge of clk_nx is aligned to rising/falling edge of clk
input                                       cke_nx,

input       signed  [IDW-1:0]               in_dat,

input               [COE_NUM*CDW-1:0]       in_coe,     // {CM-1, .... C1, C0}, M=COE_NUM
output  reg signed  [ODW-1:0]               out_dat 
);

//################################################################################
// define local varialbe and localparam
//################################################################################

reg                                         tog;
reg                                         tog_1d;
wire                                        tog_edge;
reg                 [IDW-1:0]               insert_zero_dat;
wire                [ODW-1:0]               out_dat_c;

//################################################################################
// main
//################################################################################

always@(posedge clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        tog <=`U_DLY 1'b0;
    end else if (cke==1'b1) begin
        tog <=`U_DLY ~tog;
    end else
        ;
end

always@(posedge clk_nx or negedge rst_n) begin
    if (rst_n==1'b0) begin
        tog_1d <=`U_DLY 1'b0;
    end else if (cke_nx==1'b1) begin
        tog_1d <=`U_DLY tog;
    end else
        ;
end

assign tog_edge = tog ^ tog_1d;

always@(posedge clk_nx or negedge rst_n) begin
    if (rst_n==1'b0) begin
        insert_zero_dat <=`U_DLY {IDW{1'b0}};
    end else if (cke_nx==1'b1) begin
        if (tog_edge==1'b1)
            insert_zero_dat <=`U_DLY in_dat;
        else
            insert_zero_dat <=`U_DLY {IDW{1'b0}};
    end else
        ;
end

sym_coe_fir #(
        .CDW                            (CDW                            ),
        .CFW                            (CFW                            ),
        .IDW                            (IDW                            ),
        .IFW                            (IFW                            ),
        .ODW                            (ODW                            ),
        .OFW                            (OFW                            ),
        .TAP_NUM                        (TAP_NUM                        ),	// must TAP_NUM>=2
        .COE_NUM                        (COE_NUM                        ),
        .CSCD_LEN_MAX                   (CSCD_LEN_MAX                   ),	// CSCD_LEN_MAX>=2, cascade length, if too bigger, bad Fmax
        .COE_ZERO_MAP                   (COE_ZERO_MAP                   ),	// for saving area
        .COE_ZERO_NUM                   (COE_ZERO_NUM                   ),
        .PRDCT_NUM                      (PRDCT_NUM                      ),
        .CSCD_GRP                       (CSCD_GRP                       ),
        .CSCD_LEN                       (CSCD_LEN                       ),
        .LVL_NUM                        (LVL_NUM                        ),
        .LVL_REG                        (LVL_REG                        )	// specify register for each level
) u_fir ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk_nx                         ),

        .in_vld                         (cke_nx                         ),
        .in_coe                         (in_coe                         ),	// {CM-1, .... C1, C0}, M
        .in_dat                         (insert_zero_dat                ),

        .out_vld                        (                               ),
        .out_dat                        (out_dat_c                      )
);

always@(posedge clk_nx or negedge rst_n) begin
    if (rst_n==1'b0) begin
        out_dat <=`U_DLY {ODW{1'b0}};
    end else if (cke_nx==1'b1) begin
        out_dat <=`U_DLY out_dat_c;
    end else
        ;
end

function integer bit_sum_f;
    input [COE_NUM-1:0]     vect;

    integer t;
    integer i;
    begin
        t = 0;
        for (i=0; i<COE_NUM; i=i+1) begin
            t = t + vect[i];
        end

        bit_sum_f = t;
    end
endfunction

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
